import {
  getProjectCssPath,
  getProjectImgPath,
  getProjectJsPath,
  getProjectPath,
  getProjectTmpPath,
  getTemplatesPath,
} from "../render";
import {
  fileStructureInterface,
  folderTreeType,
  gainIdFromFile,
} from "../server";
import {
  addCssFileAndQuote,
  stripHref,
  scopeCssFile,
  stripSrc,
  genCssFile,
  resolveUrl,
} from "./cssAnalyser";
import { copyIfNotExist, fileType } from "./util";
// import * as cheerio from "cheerio";
const path = require("path");
const fs = require("fs");

const Store = require("electron-store");
const store = new Store();

// src=" | src='
const getQuote = (str: string) => str.substring(4, 5);
// href=" | href='
const getQuote2 = (str: string) => str.substring(5, 6);

const duplicateSolution = (
  filePath: string,
  requestPath: string,
  fileBuffer: Buffer,
  folderTree: folderTreeType[]
): string => {
  //looking for current file relative path
  const temp = gainIdFromFile(filePath, fileBuffer);

  let fileId = Number(temp);

  const curFilePath = folderTree.find(
    (val) => val.id === fileId
  ).originalFilename;

  const requestFileOriPath =
    path.parse(store.get("uploadFolderPath")).name +
    path
      .resolve(
        path.dirname(
          path.resolve(path.dirname(store.get("uploadFolderPath")), curFilePath)
        ),
        requestPath
      )
      .replace(store.get("uploadFolderPath"), "")
      .replaceAll("\\", "/");

  const item = folderTree.find(
    (val) => val.originalFilename === requestFileOriPath
  );
  if (!item) return "cannot_find_file";
  return item?.dest.replace(store.get("projectPath"), "").substring(1);
};

export const getRequestFileName = (
  requestPath: string,
  fileObj: fileObjType,
  folderTree: folderTreeType[],
  cb: (requestFileName: string) => string // if item is not duplicate with other files
): string => {
  let requestFileName = path.parse(requestPath).base;
  // duplicate
  if (folderTree.find((val) => val.fileName === requestFileName)?.duplicate) {
    return duplicateSolution(
      fileObj.filePath,
      requestPath,
      fileObj.fileBuffer,
      folderTree
    );
  }
  // duplicate

  //not duplicated
  return cb(requestFileName);
  //not duplicated
};

const resolveLinkHref = (fileObj: fileObjType) => {
  const reg = new RegExp(/<link[^>]*?>/gi);
  const arr = fileObj.file.match(reg);
  if (arr === null) return [];
  return arr
    .map((val) => {
      let tmp = val.match(/href=('|").*?('|")/);
      if (tmp === null) {
        return {
          origin: "",
          replaceWith: "",
        };
      }
      const href = tmp[0];
      let requestPath = href.substring(6, href.length - 1);
      let requestFileName = getRequestFileName(
        requestPath,
        fileObj,
        fileObj.folderTree,
        (requestFileName) => {
          switch (fileType(requestFileName)) {
            case "css":
              return "res/css/" + requestFileName;
            default:
              return "res/img/" + requestFileName;
          }
        }
      );
      return {
        origin: val,
        replaceWith: val.replace(href, 'href="' + requestFileName + '"'),
      };
    })
    .filter((val) => val.origin !== "");
};

// const getPathName = (path: string) => path.substring(path.lastIndexOf("/") + 1);

const resolveSrcTag = (
  // file: string,
  fileObj: fileObjType,
  tag: string,
  dir: "js" | "img" | "css"
) => {
  if (dir != "js")
    fileObj.file = fileObj.file.replace(/<script(\r|\n|.)*?<\/script>/gi, "");
  const reg = new RegExp(`<${tag}[^>]*?>`, "gi");
  const arr = fileObj.file.match(reg);
  if (arr === null) return [];
  return arr
    .filter((val) => val.indexOf("src=") > -1)
    .map((val) => {
      const src = val.match(/src=('|").*?('|")/)[0];
      const requestPath = src.substring(5, src.length - 1); // requestFileName
      let requestFileName = path.basename(
        getRequestFileName(
          requestPath,
          fileObj,
          fileObj.folderTree,
          (requestFileName) => requestFileName
        )
      );

      const quote = getQuote(src);

      return {
        origin: val,
        replaceWith: val.replace(
          src,
          `src=${quote}res/${dir}/` + requestFileName + quote
        ),
      };
    });
};

const resolveFrameSrc = (fileObj: fileObjType) => {
  const iframeReg = new RegExp(/<iframe[^>]*?>/gi);
  const arr = fileObj.file.match(iframeReg);
  if (arr === null) return [];
  return arr
    .filter((val) => val.indexOf("src=") > -1 && val.indexOf("http") === -1)
    .map((val) => {
      const src = val.match(/src=('|").*?('|")/)[0];
      let requestPath = src.substring(5, src.length - 1);
      let requestFileName = path.basename(
        getRequestFileName(
          requestPath,
          fileObj,
          fileObj.folderTree,
          (requestFileName) => requestFileName
        )
      );
      // const last = attr.lastIndexOf("/");
      // if (last != -1) attr = attr.substring(last + 1);

      const quote = getQuote(src);
      return {
        origin: val,
        replaceWith: val.replace(
          src,
          `src=${quote}./` + requestFileName + quote
        ),
      };
    });
};

const resolveAnchorHref = (fileObj: fileObjType): analyserResult[] => {
  const anchorReg = new RegExp(/<a[^>]*?/gi);
  const arr = fileObj.file.match(anchorReg);
  if (arr === null) return [];
  return arr
    .filter((val) => val.indexOf("href=") !== -1 && val.indexOf("http") === -1)
    .map((val) => {
      const href = val.match(/href=('|").*?('|")/)[0];
      let requestPath = href.substring(6, href.length - 1);
      let requestFileName = path.basename(
        getRequestFileName(
          requestPath,
          fileObj,
          fileObj.folderTree,
          (requestFileName) => requestFileName
        )
      );
      const quote = getQuote2(href);
      return {
        origin: val,
        replaceWith: val.replace(
          href,
          `href=${quote}./` + requestFileName + quote
        ),
      };
    });
};

// const resolveHtmlBackground = (fileObj: fileObjType): analyserResult[] => {
//   const backgroundReg = new RegExp(
//     /background(.|\n){0,20}?url(.|\n){0,50}?;/gi
//   );
//   const arr = fileObj.file.match(backgroundReg);
//   if (arr === null) return [];
//   return arr
//     .map((val) => {
//       // if (val.match(/url\((.|\n)*?\)/gi) === null) console.log(val);
//       const matchedUrl = val.match(/url\((.|\n)*?\)/gi);
//       if (matchedUrl === null)
//         return {
//           origin: "",
//           replaceWith: "",
//         };
//       const url = matchedUrl[0];
//       let requestPath = url.substring(4, url.length - 1).trim(); //remove redundant text wrap
//       //remove quota
//       if (requestPath.startsWith('"'))
//         requestPath = requestPath.substring(1, requestPath.length - 1);

//       let requestFileName = path.basename(
//         getRequestFileName(
//           requestPath,
//           fileObj,
//           fileObj.folderTree,
//           (requestFileName) => requestFileName
//         )
//       );
//       // const last = stripUrl.lastIndexOf("/");
//       // if (last != -1) stripUrl = stripUrl.substring(last + 1);
//       return {
//         origin: url,
//         replaceWith: `url("res/img/${requestFileName}")`,
//       };
//     })
//     .filter((val) => val.origin !== "");
// };

export interface analyserResult {
  origin: string;
  replaceWith: string;
}

const resolveHtmlComment = (indexFile: string): analyserResult[] => {
  return (
    indexFile
      .match(/<!--(.|\r|\n)*?-->/g)
      ?.map((val) => ({ origin: val, replaceWith: "" })) || []
  );
};

export type fileObjType = {
  filePath: string;
  file: string;
  fileBuffer: Buffer;
  folderTree: folderTreeType[];
};
export const htmlReLink = (fileObj: fileObjType): analyserResult[] => {
  // match all link tag
  const linkReplacer = resolveLinkHref(fileObj);
  // console.log(linkReplacer, "linkReplacer");
  // match all script tag
  const scriptReplacer = resolveSrcTag(fileObj, "script", "js");
  // match all img tag
  const imgReplacer = resolveSrcTag(fileObj, "img", "img");
  // match all source tag
  const sourceReplacer = resolveSrcTag(fileObj, "source", "img");
  // match all video tag
  const videoReplacer = resolveSrcTag(fileObj, "video", "img");
  // match all audio tag
  const audioReplacer = resolveSrcTag(fileObj, "audio", "img");
  // match all iframe tag
  const iframeReplacer = resolveFrameSrc(fileObj);
  // match all anchor tag
  const anchorReplacer = resolveAnchorHref(fileObj);
  // match all background
  // const backgroundReplacer = resolveHtmlBackground(fileObj);
  const backgroundReplacer = resolveUrl(fileObj, {
    css: "res/css",
    img: "res/img",
  });
  // match all comment
  const commentReplacer = resolveHtmlComment(fileObj.file);

  return [
    ...linkReplacer,
    ...scriptReplacer,
    ...imgReplacer,
    ...sourceReplacer,
    ...videoReplacer,
    ...audioReplacer,
    ...iframeReplacer,
    ...anchorReplacer,
    ...backgroundReplacer,
    // reserve last id comment
    ...commentReplacer.slice(0, commentReplacer.length - 1),
  ];
};

type jss = "js" | "css";

const getRealPath = (type: jss, index: string): string => {
  return getProjectPath() + "\\" + `res/${type}/integration${index}.${type}`;
};

const findContent = (file: string, type: jss): string[] => {
  const res =
    type === "css"
      ? file.match(new RegExp(`<style(.|\n|\r)*?>(.|\n|\r)*?<\/style>`, "ig"))
      : file
          .match(new RegExp(`<script(.|\n|\r)*?>(.|\n|\r)*?<\/script>`, "ig"))
          ?.filter(
            (str) =>
              !/^<script(.|\n|\r)*?src=(.|\n|\r)*?>/.test(
                str.substring(0, str.indexOf(">") + 1)
              )
          );

  return res || [];
};

const removeTag = (content: string, tagName: string): string => {
  const reg = new RegExp(`(<${tagName}(.|\n|\r)*?>|</${tagName}>)`, "ig");
  return content.replace(reg, "");
};

const quoteCssContent = (file: string, index: number) => {
  let quote = `<link rel="stylesheet" type="text/css" href="res/css/integration${index}.css" />`;
  if (file.indexOf("<head>") === -1) {
    throw new Error("cannot find head!");
  }
  if (file.indexOf(quote) > -1) return file;
  return file.replace("</head>", `\n\t${quote}\n</head>`);
};

const quoteJsContent = (
  file: string,
  replacement: string[],
  index: number
): string => {
  replacement.forEach((val, seq) => {
    file = file.replace(
      `<!-- js_${index}_${seq} -->`,
      `\n<script src="res/js/integration${index}_${seq}.js"></script>\n`
    );
  });
  return file;
};

const appendToFile = (filePath: string, content: string) => {
  fs.appendFileSync(
    filePath,
    "/* update from " + new Date().valueOf() + "*/\n" + content,
    "utf8"
  );
};

export const htmlIntegration = (file: string, index: number) => {
  // find js content
  const jsContent = findContent(file, "js");
  // find css content
  const cssContent = findContent(file, "css");
  // remove js content in html
  jsContent.forEach((val, seq) => {
    file = file.replace(val, `<!-- js_${index}_${seq} -->`);
  });
  // remove css content in html
  cssContent.forEach((val) => {
    file = file.replace(val, "");
  });
  // remove script tag
  const jsContentNoTag = jsContent.map((val) => removeTag(val, "script"));
  // remove style tag
  const cssContentNoTag = cssContent.map((val) => removeTag(val, "style"));
  // append content to created js file
  jsContentNoTag.forEach((val, seq) => {
    appendToFile(getRealPath("js", index + "_" + seq), val);
  });
  // append content to created css file
  appendToFile(
    getRealPath("css", index + ""),
    cssContentNoTag
      .join("\n/*------------*/\n")
      .replace(/res\/img\//g, "../img/")
  );
  // quote in html if file alreay exist then skip this step
  file = quoteJsContent(file, jsContent, index);
  // quote in html, same to last
  file = quoteCssContent(file, index);

  return file;
};

export const changeTitle = (titleName: string, file: string): string => {
  return (file = file
    .replace(/<title(.|\n)*?<\/title>/i, "")
    .replace(/<\/head>/, `<title>${titleName}</title>\n</head>`));
};

//-----------------------------------------------

const removeUnhealthAnchor = (file: string) => {
  const reg = /<a(\r|\n|.)*?>/gi;
  const qualified = file.match(reg)?.filter((val) => val.indexOf("http") > -1);
  qualified?.forEach((val) => {
    // replace href with #
    file = file.replace(
      val,
      val.replace(/href=('|").*?('|")/, 'href="########"')
    );
  });
  return file;
};

const removeUnhealthScrapBook = (file: string) => {
  const reg = /data-scrapbook.*?("|').*?("|')/gi;
  return file.replace(reg, "");
};

export const cleanHtmlLinks = (file: string, index: number) => {
  // match all useless anchor
  file = removeUnhealthAnchor(file);
  // match all data-scrape-book ...
  file = removeUnhealthScrapBook(file);

  return file;
};

const find = <T>(arr: T[], callback: (entity: T) => boolean) => {
  for (let index = 0; index < arr.length; index++) {
    if (callback(arr[index])) break;
  }
};

export const unlinkImg = (structure: fileStructureInterface) => {
  const removeList: string[] = [];

  structure.img.forEach((imgName) => {
    let flag = false; // false: not found
    if (fs.statSync(imgName).isDirectory()) return;
    find(structure.html, (val) => {
      const filePath = path.resolve(getProjectPath(), val);
      flag = fs.readFileSync(filePath).toString().indexOf(imgName) > -1;
      return flag;
    });

    !flag &&
      find(structure.css, (val) => {
        const filePath = path.resolve(getProjectCssPath(), val);
        flag = fs.readFileSync(filePath).toString().indexOf(imgName) > -1;
        return flag;
      });

    !flag &&
      find(structure.js, (val) => {
        const filePath = path.resolve(getProjectJsPath(), val);
        flag = fs.readFileSync(filePath).toString().indexOf(imgName) > -1;
        return flag;
      });

    if (!flag) {
      removeList.push(imgName);
    }
  });

  removeList.forEach((val) => {
    const from = path.resolve(getProjectImgPath(), val);
    const to = path.resolve(getProjectTmpPath(), val);
    if (!fs.existsSync(from)) {
      console.log("dir [from] error: " + from);
      return;
    }
    fs.copyFileSync(from, to);
    fs.unlinkSync(from);
  });
};

const addJsFileAndQuote = (
  content: string,
  indexFile: string,
  fileName: string
): string => {
  if (!fileName.endsWith(".js")) {
    fileName = fileName += ".js";
  }
  const jsPath = path.resolve(getProjectJsPath(), fileName);
  fs.writeFileSync(jsPath, content);
  let quote = `<script src="res/js/${fileName}"></script>`;
  if (indexFile.indexOf(quote) > -1) return indexFile;
  return indexFile.replace("</body>", `\n\t${quote}\n</body>`);
};

const appendToBody = (indexFile: string, element: string): string => {
  return indexFile.replace("</body>", `${element}\n</body>`);
};

const appendToHead = (indexFile: string, element: string): string => {
  return indexFile.replace("</head>", `${element}\n</head>`);
};

const removeAudio = (indexFile: string): string => {
  indexFile
    .match(/<audio(.|\r|\n)*?<\/audio>/gi)
    ?.forEach((el) => (indexFile = indexFile.replace(el, "")));
  return indexFile;
};

export const addMusicToIndex = (file: string): string => {
  //check music file existence
  const list = fs.readdirSync(getProjectImgPath());
  let mp3FileName = list.find((val) => val.endsWith("music.mp3"));
  if (!mp3FileName) {
    mp3FileName = "music.mp3";
    fs.copyFileSync(
      path.resolve(getTemplatesPath(), "music/music.mp3"),
      path.resolve(getProjectImgPath(), mp3FileName)
    );
  }
  copyIfNotExist(
    path.resolve(getTemplatesPath(), "music/myMusic.png"),
    path.resolve(getProjectImgPath(), "myMusic.png")
  );
  // copyAndThrowErrIfExist(
  //   path.resolve(getTemplatesPath(), "music3.js"),
  //   path.resolve(getProjectJsPath(), "music3.js")
  // );
  if (file.indexOf("<head>") === -1) {
    throw new Error("cannot find head!");
  }
  file = addJsFileAndQuote(
    fs
      .readFileSync(path.resolve(getTemplatesPath(), "music/music3.js"))
      .toString(),
    file,
    "music3.js"
  );

  file = addCssFileAndQuote(
    fs
      .readFileSync(path.resolve(getTemplatesPath(), "music/music3.css"))
      .toString(),
    file,
    "music3.css"
  );

  // remove audio element
  file = removeAudio(file);
  // create audio element
  const audioElement = fs
    .readFileSync(path.resolve(getTemplatesPath(), "music/music3.html"))
    .toString();
  // append to body
  file = appendToBody(file, audioElement);

  // if (
  //   fs
  //     .readdirSync(getProjectImgPath())
  //     .findIndex((val) => val === "music.mp3") === -1
  // ) {
  //   fs.copyFileSync(
  //     path.resolve(getTemplatesPath(), "music/music.mp3"),
  //     path.resolve(getProjectImgPath(), "music.mp3")
  //   );
  // }

  return file;
};

//-----------------------------------------------

/**
 *
 * @param cssArr (link href="xxx.css" rel="stylesheet" type="text/css" ) | (style) ... (/style)
 * @param mark unique id of css file
 * @param type "link" | "style"
 * @returns [(link href="xxx.2css" rel="stylesheet" type="text/css" ),....]
 */
const styleBatch = (
  cssArr: string[],
  mark: string,
  type: "link" | "style"
): string[] => {
  return cssArr.map((val) => {
    const fileName =
      type === "link" ? stripHref(val).name : genCssFile("/*placeholder*/"); // <style>
    if (!fileName) {
      console.log("cannot convert css: " + fileName);
      return;
    }

    const filePath = path.resolve(getProjectCssPath(), fileName);

    if (!fs.existsSync(filePath)) return val;
    if (type === "style") {
      val = val.replace(/<\/{0,1}style(.|\r|\n)*?>/gi, "");
    }
    const cssFile =
      type === "link" ? fs.readFileSync(filePath).toString() : val;
    const newPath =
      type === "link" ? filePath.replace(".css", "2.css") : filePath;
    fs.writeFileSync(newPath, scopeCssFile(cssFile, mark));

    return `<link href="res/css/${path.parse(newPath).base}" rel="stylesheet">`;
  });
};

//$ cheerio.CheerioAPI
// export const mergeIframe2 = (document: string): string => {
//   const $ = cheerio.load(document);
//   $("iframe").each((seq, el) => {
//     const src = el.attribs.src;
//     if (src.startsWith("http")) return;
//     const iframePath = path.resolve(getProjectPath(), src);
//     if (!fs.existsSync(iframePath) || fs.statSync(iframePath).isDirectory()) {
//       console.log("cannot find iframe file: " + src);
//       return;
//     }
//     console.log(iframePath, "src");
//     const frameFile = fs.readFileSync(iframePath).toString();
//     const dependencies: string[] = [];
//     const query = cheerio.load(frameFile);
//     query("link").each(function (seq, ele) {
//       dependencies.push(ele.attribs.href);
//     });
//     let style = "";
//     query("style").each(function (seq, ele) {
//       style += query(ele).text();
//     });
//     dependencies.push(
//       `<link href="res/css/${genCssFile(style)}" rel="stylesheet"/>`
//     );

//     query("body")
//       .children()
//       .each(function (seq, ele) {
//         const classes = ele.attribs.class;
//         if (classes) {
//           ele.attribs.class = classes
//             .split(" ")
//             .filter((val) => val)
//             .map((val) => `frame_${val}`)
//             .join(" ");
//         }
//       });

//     $(el).replaceWith(
//       `<div id="iframeStyle${seq}">${query("body").children().html()}</div>`
//     );
//   });

//   console.log($("html").html());

//   return "";
// };
// export const mergeIframe = (indexFile: string): string => {
//   // match all iframe tag
//   const arr = indexFile
//     .match(/<iframe(.|\r|\n)*?<\/iframe>/gi)
//     .filter((val) => val.indexOf("http") === -1);

//   const dependencies: string[] = [];

//   const replacer = arr
//     .map((val, seq) => {
//       const iframeSrc = path.resolve(getProjectPath(), stripSrc(val).name);

//       if (!fs.existsSync(iframeSrc)) {
//         console.log("cannot find correspond iframe file: " + iframeSrc);
//         return {
//           origin: "",
//           replaceWith: "",
//         };
//       }
//       const frameFile = fs.readFileSync(iframeSrc).toString();
//       const body = frameFile.match(/<body(.|\r|\n)*?<\/body>/)[0];
//       const strip = body.substring(
//         body.indexOf(">") + 1,
//         body.lastIndexOf("<")
//       );
//       let wrap = val.replace(/iframe/g, "div");
//       const className = `iframeStyle${seq}`;
//       wrap = wrap.replace("div", `div id="${className}"`);

//       let replaceWith =
//         wrap.substring(0, wrap.indexOf(">") + 1) + strip + "</div>";
//       //remove iframe file
//       fs.unlinkSync(iframeSrc);

//       //collect dependencies
//       const js = frameFile.match(/<script(.|\r|\n)*?<\/script>/gi);
//       if (js) dependencies.push(...js);
//       const css = frameFile.match(/<style(.|\r|\n)*?<\/style>/gi);
//       if (css) dependencies.push(...styleBatch(css, className, "style"));
//       const link = frameFile.match(/<link(.|\r|\n)*?>/gi);
//       if (link) dependencies.push(...styleBatch(link, className, "link"));

//       // remove old dependencies
//       // replaceWith.replace()
//       js?.forEach((val) => {
//         replaceWith = replaceWith.replace(val, "");
//       });
//       css?.forEach((val) => {
//         replaceWith = replaceWith.replace(val, "");
//       });
//       link?.forEach((val) => {
//         replaceWith = replaceWith.replace(val, "");
//       });
//       //collect dependencies

//       //separate class \ id from parent environment by add prefix to class and id selector

//       return {
//         origin: val,
//         replaceWith,
//       };
//     })
//     .filter((val) => val.origin !== "");

//   replacer.forEach((val) => {
//     indexFile = indexFile.replace(val.origin, val.replaceWith);
//   });

//   // console.log(dependencies, "denpendecies");

//   indexFile = appendToHead(indexFile, dependencies.join("\n"));

//   return indexFile;
//   //  arr.map((val) => ({ origin: val, replaceWith: "" }));
// };

const getFirstTag = (paragraph: string, tagName: string) => {
  const reg = new RegExp(`<${tagName}[^>]*>`, "gi");
  const res = paragraph.match(reg);
  if (res) return res[0];
  return "";
};

const getWholeTag = (paragraph: string, tagName: string) => {
  const reg = new RegExp(`<${tagName}(.|\r|\n)*?\/${tagName}>`, "gi");
  const res = paragraph.match(reg);
  if (res) return res[0];
  return "";
};

const getAttribute = (tag: string, key: string) => {
  const res = tag.match(new RegExp(`${key}=("|')(.{0,30}?)("|')`, "gi"));
  if (!res) return "";
  return res[0];
};

const rewriteFile = (file: string): string => {
  let res = "";
  let start = 0;
  let last = 0;

  while (true) {
    last = file.indexOf("class=", start);
    const quote = file[last + 1];
    if (last === -1) break;
    res += file.substring(start, last);
    start = file.indexOf(quote, last);
    if (start === -1) break;
    res += file.substring(start + 1, last);
  }

  return file;
};
